package sketch.doc;

import java.awt.Graphics2D;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import sketch.ink.SampledPoint;
import sketch.ink.Stroke;
import sketch.parse.Parser;
import sketch.parse.Parser.DOMAIN;
import sketch.seg.CurveSegmenter;
import sketch.seg.PrimApproximator;
import sketch.seg.cd.CDStaticKH;
import sketch.seg.cd.CornerDetector;
import sketch.shape.Shape;
import sketch.shape.geom.prim.Point;

public class SketchContainer {

	private static SketchContainer instance;

	private Sketch sketch;

//	private Stroke selectedStroke;

	private List<Stroke> selectedStrokes;

//	private Shape selectedShape;

	private List<Shape> selectedShapes;

//	private Concept selectedConcept;
//
//	private Relation selectedRelation;



	private CornerDetector cd = new CDStaticKH();

	public void setCD(CornerDetector cd) {
//		for (Stroke s : open().getStrokes()) {
//			s.setCD(cd);
//		}
		this.cd = cd;
		cornerDetect();
	}

	public CornerDetector getCD() {
		return cd;
	}

	private SketchContainer() {
		selectedStrokes = new ArrayList<Stroke>();
		selectedShapes = new ArrayList<Shape>();
	}

	public static SketchContainer get() {
		if (instance == null) {
			instance = new SketchContainer();
		}
		return instance;
	}

	public Sketch open() {
		if (sketch == null)
			sketch = new Sketch();
		return sketch;
	}

	public void close() {
		sketch = null;
	}

	public List<Stroke> getSelectedStrokes() {
		return selectedStrokes;
	}

	public void setSelectedStrokes(List<Stroke> s) {
		selectedStrokes = s;
	}

	public void setSelectedStrokesByIds(int[] ids) {
		clearStrokeSelection();
		for (int i : ids) {
			selectStrokeById(i);
		}
	}

	public void selectStrokeById(int id) {
		selectStroke(open().getStrokeById(id));
	}

	public void selectStroke(Stroke s) {
		if (!selectedStrokes.contains(s))
			selectedStrokes.add(s);
	}

	public List<Shape> getSelectedShapes() {
		return selectedShapes;
	}

	public void setSelectedShapes(List<Shape> sh) {
		selectedShapes = sh;
	}

	public void setSelectedShapesByIds(int[] ids) {
		clearShapeSelection();
		for (int i : ids) {
			selectShapeById(i);
		}
	}

	public void selectShapeById(int id) {
		selectShape(open().getShapeById(id));
	}

	public void selectShape(Shape sh) {
		if (!selectedShapes.contains(sh))
			selectedShapes.add(sh);
	}

	public void clearStrokeSelection() {
		selectedStrokes = new ArrayList<Stroke>();
	}

	public void clearShapeSelection() {
		selectedShapes = new ArrayList<Shape>();
	}

	public void reset() {
		sketch = new Sketch();
		clearShapeSelection();
		clearStrokeSelection();

	}

	public void addInk(Stroke s) {
		open().appendInk(s);
		getCD().setPoints(s.getPoints());
		s.setCorners(getCD().getCorners());
	}

	public void addStroke(Stroke s) {
		open().appendStroke(s);
		getCD().setPoints(s.getPoints());
		s.setCorners(getCD().getCorners());
	}

	public void addStrokes(Collection c) {
		for (Object o : c) {
			if (o instanceof Stroke) {
				Stroke s = (Stroke) o;
				addStroke(s);
			}
		}
	}

	public List<Stroke> getStrokes() {
		return open().getStrokes();
	}

	public List<Shape> getShapes() {
		return open().getShapes();
	}

	public void parse() {
		Parser parser = Parser.get();
		parser.setSketch(open());
		parser.parse();

		clearShapeSelection();
	}

	public void parse2() {
		Parser parser = Parser.get();
		parser.setSketch(open());
		DOMAIN domain = parser.getDomain();
		parser.setDomain(DOMAIN.link);
		parser.parse();
		parser.parse();
		parser.parse();
		//TODO

		clearShapeSelection();
		parser.setDomain(domain);
	}

	public void off_line_parse() {

		List<Stroke> ss = getStrokes();
		reset();

		for (Stroke s : ss) {
			addStroke(s);
			segment();
			primApproximate();
			parse();
		}

	}

	public void primApproximate() {
		PrimApproximator pa = new PrimApproximator();

		for (Iterator<Stroke> it = open().getStrokes().iterator(); it.hasNext(); ) {
			Stroke stroke = it.next();
			if (!stroke.isUsed()) {
				pa.setInputPoints(stroke.getPoints());
				open().appendShape(pa.getPrim());
				stroke.setUsed(true);
			}
		}
		clearStrokeSelection();
	}

	public void cornerDetect() {
		for (Stroke s : open().getStrokes()) {
			if (s.isUsed())
				continue;

			getCD().setPoints(s.getPoints());
			s.setCorners(getCD().getCorners());
		}
	}

	public void segment() {
		List<Stroke> list = new ArrayList<Stroke>();

		CurveSegmenter cs = new CurveSegmenter();
		for (Iterator<Stroke> it = open().getStrokes().iterator(); it.hasNext(); ) {
			Stroke stroke = it.next();

			cs.setInputPoints(stroke.getPoints(), stroke.getCorners());
			List<List<SampledPoint>> fragments = cs.getFragments();

			if (fragments.size() < 2)
				continue;

			for (List<SampledPoint> frag : fragments) {
				Stroke s = new Stroke();
				s.setId(Sketch.generateId());
				s.setPoints(frag);
				s.setOriginal(stroke.getOriginal());
				list.add(s);
			}
			it.remove();
		}
		addStrokes(list);
		clearStrokeSelection();
	}

	public void drawShapes(Graphics2D g2) {
		for (Iterator<Shape> it = open().getShapes().iterator(); it.hasNext(); ) {
			it.next().draw(g2);
		}
	}

	public void drawRawInk(Graphics2D g2) {
		for (Iterator<Stroke> it = open().getStrokes().iterator(); it.hasNext(); ) {
			it.next().draw(g2);
		}
	}

	public void drawInk(Graphics2D g2) {
		for (Iterator<Stroke> it = open().getStrokes().iterator(); it.hasNext(); ) {
			Stroke stroke = it.next();
			if (!stroke.isUsed())
				stroke.draw(g2);
		}
	}

	public void drawSelectedStroke(Graphics2D g2) {
		for (Stroke stroke : getSelectedStrokes())
			if (stroke != null) {
	        	stroke.drawSelected(g2);
			}
	}

	public void drawSelectedShape(Graphics2D g2) {
		for (Shape shape : getSelectedShapes())
			if (shape != null) {
				shape.drawSelected(g2);
			}
	}

	public void saveToFile(File file, int option) {
		open().saveToFile(file, option);
	}

	public void readFromFile(File file) {
		open().readFromFile(file);
	}

	public void removeSelectedStrokes() {
		open().removeStrokes(getSelectedStrokes());
	}

	public void removeSelectedShapes() {
		open().removeShapes(getSelectedShapes());
	}

	public void moveSelectedStrokesTo(Point target) {
		List<Point> ps = new ArrayList<Point>();
		for (Stroke stroke : getSelectedStrokes()) {
			ps.add(stroke.getCenter());
		}

		double x = 0, y = 0;
		for (Point p : ps) {
			x += p.getX();
			y += p.getY();
		}
		x /= ps.size();
		y /= ps.size();

		for (Stroke stroke : getSelectedStrokes()) {
			stroke.move(target.subtract(new Point(x, y)));
		}

	}

//	public Concept getSelectedConcept() {
//		return selectedConcept;
//	}
//
//	public void setSelectedConcept(Concept selectedConcept) {
//		this.selectedConcept = selectedConcept;
//	}
//
//	public Relation getSelectedRelation() {
//		return selectedRelation;
//	}
//
//	public void setSelectedRelation(Relation selectedRelation) {
//		this.selectedRelation = selectedRelation;
//	}
}
